package com.stars.easyms.logger.config;

import com.stars.easyms.logger.enums.LogFrameworkType;
import com.stars.easyms.base.util.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import org.apache.catalina.core.AccessLogAdapter;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;

import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * <p>className: EasyMsLoggerInitializer</p>
 * <p>description: 日志模块初始化类</p>
 *
 * @author guoguifang
 * @date 2019/8/15 21:53
 * @since 1.3.0
 */
@Slf4j
public class EasyMsLoggerInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>,
        ApplicationContextAware, Ordered {

    private static final AtomicBoolean REDEFINED = new AtomicBoolean(false);

    private static final AtomicBoolean INITED = new AtomicBoolean(false);

    private static boolean redefineSuccess;

    /**
     * 用于springboot模式启动时初始化日志模块
     *
     * @param applicationContext applicationContext
     */
    @Override
    public void initialize(@NonNull ConfigurableApplicationContext applicationContext) {
        EasyMsLoggerInitializer.redefine();
        EasyMsLoggerInitializer.init(applicationContext);
    }

    /**
     * 用于springMvc模式启动时初始化日志模块
     *
     * @param applicationContext applicationContext
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        EasyMsLoggerInitializer.redefine();
        EasyMsLoggerInitializer.init((ConfigurableApplicationContext) applicationContext);
    }

    static boolean redefine() {
        if (REDEFINED.compareAndSet(false, true)) {

            // 获取类加载器及开启byteBuddyAgent
            ByteBuddyAgent.install();
            try {
                // 判断当前使用的日志框架是哪种，然后重新定义
                redefineSuccess = redefineStaticLoggerBinder(Thread.currentThread().getContextClassLoader());
            } catch (Exception e) {
                EasyMsLoggerHelper.setExceptionDesc(ExceptionUtil.getExceptionDesc(e));
            }
        }
        return redefineSuccess;
    }

    /**
     * 创建日志配置属性对象
     */
    static void init(@NonNull ConfigurableApplicationContext applicationContext) {
        if (INITED.compareAndSet(false, true)) {
            // 初始化日志属性
            EasyMsLoggerHelper.initLoggerProperties(applicationContext, redefineSuccess);
        }
    }

    private static boolean redefineStaticLoggerBinder(final ClassLoader classLoader) throws IllegalAccessException {
        LogFrameworkType logFrameworkType = Arrays.stream(LogFrameworkType.values())
                .sorted(Comparator.comparingInt(LogFrameworkType::getPriority))
                .filter(l -> ClassUtils.isPresent(l.getGuide(), classLoader))
                .findFirst()
                .orElse(null);
        if (logFrameworkType != null) {
            EasyMsLoggerHelper.setLogFrameworkType(logFrameworkType);
            new ByteBuddy()
                    .redefine(logFrameworkType.getRedefineClass())
                    .name(org.slf4j.impl.StaticLoggerBinder.class.getName())
                    .make()
                    .load(org.slf4j.impl.StaticLoggerBinder.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
            // 如果使用的是logback需要重新初始化
            if (logFrameworkType.getRedefineClass() == com.stars.easyms.logger.support.logback.redefine.EasyMsStaticLoggerBinder.class) {
                FieldUtils.writeField(org.slf4j.impl.StaticLoggerBinder.getSingleton(), "initialized", false, true);
                org.slf4j.impl.StaticLoggerBinder.getSingleton().getLoggerFactory();
            }
            return true;
        }
        return false;
    }

    /**
     * springboot模式下ApplicationContextInitializer的顺序
     */
    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

}